package main

import (
	"fmt"
	"net"
	"strings"
	"time"
)

var (
	onlineClient = make(map[string]Client) //记录在线用户信息
	message      = make(chan string)       //公共信息chan，各个用户向其写入信息，Message()接着处理
)

//Client 记录用户地址，名称，消息管道
type Client struct {
	C    chan string
	Name string
	Addr string
}

//HandleConn 处理每个socket连接
func HandleConn(conn net.Conn) {
	defer conn.Close()

	clientAddr := conn.RemoteAddr().String()

	client := &Client{
		C:    make(chan string),
		Name: clientAddr,
		Addr: clientAddr,
	}
	onlineClient[client.Name] = *client

	go sendMsgToClient(conn, client)

	message <- makeMsg(client, " login")

	hasData := make(chan bool)
	isQuit := make(chan bool)

	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf)
			if n == 0 {
				isQuit <- true
				fmt.Printf("检测到客户端:%s退出\n", client.Name)
				return
			}
			if err != nil {
				fmt.Println("conn.Read err:", err)
				return
			}
			// 将读到的用户消息，保存到msg中，string 类型
			msg := string(buf[:n-1])

			// 提取在线用户列表
			if msg == "who" && len(msg) == 3 {
				conn.Write([]byte("online user list:\n"))
				// 遍历当前 map ，获取在线用户
				for _, user := range onlineClient {
					userInfo := user.Addr + ":" + user.Name + "\n"
					conn.Write([]byte(userInfo))
				}
				// 判断用户发送了 改名 命令
			} else if len(msg) >= 8 && msg[:6] == "rename" { // rename|
				newName := strings.Split(msg, "|")[1] // msg[8:]
				client.Name = newName                 // 修改结构体成员name
				onlineClient[client.Name] = *client   // 更新 onlineMap
				conn.Write([]byte("rename successful\n"))
			} else {
				// 将读到的用户消息，写入到message中。
				message <- makeMsg(client, msg)
			}
			hasData <- true
		}
	}()

	// 保证 不退出
	for {
		// 监听 channel 上的数据流动
		select {
		case <-isQuit:
			delete(onlineClient, client.Addr)    // 将用户从 online移除
			message <- makeMsg(client, "logout") // 写入用户退出消息到全局channel
			return
		case <-hasData:
			// 什么都不做。 目的是重置 下面 case 的计时器。
		case <-time.After(time.Second * 300): //300秒倒计时
			delete(onlineClient, client.Addr)                  // 将用户从 online移除
			message <- makeMsg(client, "300s time out leaved") // 写入用户退出消息到全局channel
			return
		}
	}

}

//makeMsg 封装产生消息
func makeMsg(client *Client, msg string) (buf string) {
	buf = "[" + client.Addr + "] " + client.Name + " : " + msg
	return
}

//sendMsgToClient 从用户消息管道中读取消息，再发给用户端
func sendMsgToClient(conn net.Conn, client *Client) {

	for msg := range client.C {
		conn.Write([]byte(msg + "\n"))
	}

}

//Message 从公共消息message管道中读取消息，写入到每个用户消息通道中，消息中转站，解耦合
func Message() {
	for {
		msg := <-message

		for _, cli := range onlineClient {
			cli.C <- msg
		}
	}
}

//主go程，监听生成Scoket，初始化构建信息处理模块，并发产生处理go程
func main() {
	listener, err := net.Listen("tcp", "127.0.0.1:9988")
	if err != nil {
		fmt.Println("listen err")
		return
	}
	defer listener.Close()

	go Message()

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("accept err")
			return
		}

		go HandleConn(conn)
	}
}
